package edu.mse.camel.client.posterboard.ui.editor;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.emf.ecore.change.provider.ChangeItemProviderAdapterFactory;
import org.eclipse.emf.ecore.provider.EcoreItemProviderAdapterFactory;
import org.eclipse.emf.edit.provider.ComposeableAdapterFactory;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory;
import org.eclipse.gef.ContextMenuProvider;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditDomain;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.KeyHandler;
import org.eclipse.gef.KeyStroke;
import org.eclipse.gef.MouseWheelHandler;
import org.eclipse.gef.MouseWheelZoomHandler;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.actions.GEFActionConstants;
import org.eclipse.gef.ui.actions.ZoomInAction;
import org.eclipse.gef.ui.actions.ZoomOutAction;
import org.eclipse.gef.ui.parts.GraphicalEditorWithFlyoutPalette;
import org.eclipse.gef.ui.parts.TreeViewer;
import org.eclipse.jface.action.IAction;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;

import com.cea.papyrus.core.actions.AdaptDimension;
import com.cea.papyrus.core.actions.ExtendedActionRegistry;
import com.cea.papyrus.core.actions.delete.DeleteFromModelAction;
import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.editor.abstracteditor.PapyrusGraphicalViewerKeyHandler;
import com.cea.papyrus.core.presentation.UMLExtendedItemProviderAdapterFactory;
import com.cea.papyrus.diagraminterchange2.di2.provider.Di2ItemProviderAdapterFactory;

import edu.mse.camel.client.eventbus.pubsub.EventManager;
import edu.mse.camel.client.eventbus.pubsub.ISubscriber;
import edu.mse.camel.client.events.EventIDs;
import edu.mse.camel.client.events.IEvent;
import edu.mse.camel.client.log.Debug;
import edu.mse.camel.client.posterboard.posterboard.Posterboard;
import edu.mse.camel.client.posterboard.posterboard.provider.PosterboardItemProviderAdapterFactory;
import edu.mse.camel.client.posterboard.ui.PosterboardPlugin;
import edu.mse.camel.client.posterboard.ui.actions.DeleteFromPosterboardAction;
import edu.mse.camel.client.posterboard.ui.adapters.UMLToolAdapter;
import edu.mse.camel.client.posterboard.ui.adapters.WhiteboardAdapter;
import edu.mse.camel.client.posterboard.ui.editor.dnd.PBUMLDiagramDropTargetListener;
import edu.mse.camel.client.posterboard.ui.editpart.PosterBoardEditPartFactory;
import edu.mse.camel.client.posterboard.ui.emflistener.DiagramListener;
import edu.mse.camel.client.posterboard.ui.handlers.GenericReceiveEventHandler;
import edu.mse.camel.client.posterboard.ui.handlers.ModelUpdateEventHandler;
import edu.mse.camel.client.posterboard.ui.treeoutline.PosterboardTreeOutlinePage;
import edu.mse.camel.client.session.team.TEAMMEMBER_STATUS;
import edu.mse.camel.client.session.team.TeamList;
import edu.mse.camel.client.whiteboard.gef.whiteboard.provider.WhiteboardItemProviderAdapterFactory;

public class PosterBoardEditor extends GraphicalEditorWithFlyoutPalette 
					implements ITabbedPropertySheetPageContributor {

	public static final String ID = "edu.mse.camel.client.posterboard.ui.editor.PosterBoard"; //$NON-NLS-1$
	
	public static final Color diagramColor = ColorConstants.lightGray;

	public static final String POSTERBOARD_FILE_EXTN = "posterboard";
	
	//public boolean isSource;
	
	/** Palette component, holding the tools and shapes. */
	private PaletteRoot PALETTE_MODEL;
	
	protected PosterBoardModelManager pbModelManager;
	
	protected ComposedAdapterFactory adapterFactory;
	
	private Posterboard posterboard;

	private ExtendedActionRegistry pbActionRegistry;
	
	protected GenericReceiveEventHandler genericHandler;
	
	private ModelUpdateEventHandler updateHandler;

	private DiagramListener dListener;

	/**
	 * For now we use Papyrus' key handler
	 */
	private PapyrusGraphicalViewerKeyHandler sharedKeyHandler;
	
	private ZoomManager zoomManager;
	
	private CamelModelUpdateSerialNumber serialNumber = new CamelModelUpdateSerialNumber();
	
	public PosterBoardEditor() {
		super();
		setEditDomain(new DefaultEditDomain(this));
		
		// init Adapter Factory
		List<ComposeableAdapterFactory> factories = new ArrayList<ComposeableAdapterFactory>();
		factories.add(new UMLExtendedItemProviderAdapterFactory());
		factories.add(new ResourceItemProviderAdapterFactory());
		factories.add(new Di2ItemProviderAdapterFactory());
		factories.add(new EcoreItemProviderAdapterFactory());
		factories.add(new ReflectiveItemProviderAdapterFactory());
		//darpan
		factories.add(new ChangeItemProviderAdapterFactory());
		factories.add(new PosterboardItemProviderAdapterFactory());
		factories.add(new WhiteboardItemProviderAdapterFactory());

		adapterFactory = new ComposedAdapterFactory(factories);
		
		// creation of the associated modelmanager
		pbModelManager = new PosterBoardModelManager(adapterFactory);
		
		updateHandler = new ModelUpdateEventHandler(pbModelManager, serialNumber);

		genericHandler = new GenericReceiveEventHandler();

		this.pbActionRegistry = 
			new ExtendedActionRegistry(EditorUtil.createParentRegistry(this));
		
	}

	@Override
	protected void configureGraphicalViewer() {
		super.configureGraphicalViewer();

		GraphicalViewer viewer = super.getGraphicalViewer();
		initializeToolAdapters();
		PosterBoardEditPartFactory factory = 
			new PosterBoardEditPartFactory(pbModelManager, UMLToolAdapter.INSTANCE);
		viewer.setEditPartFactory(factory);
		
		PBScalableRootEditPart root = new PBScalableRootEditPart();
		
		viewer.setRootEditPart(root);
		viewer.setKeyHandler(getCommonKeyHandler());
		
		// Scroll-wheel Zoom
		
		setZoom(viewer, root);
		
		// configure the context menu provider
		PosterBoardContextMenuProvider cmProvider =
			new PosterBoardContextMenuProvider(viewer, getActionRegistry());
		cmProvider.addHelper(new PapyrusContextMenuHelper(viewer, getActionRegistry()));
		viewer.setContextMenu((ContextMenuProvider) cmProvider);
		getSite().registerContextMenu(cmProvider, viewer);
	}
	
	private void initializeToolAdapters() {
		UMLToolAdapter.INSTANCE.initialize(pbModelManager);
		WhiteboardAdapter.INSTANCE.initialize(pbModelManager);
	}

	@SuppressWarnings({ "unchecked", "deprecation" })
	private void setZoom(GraphicalViewer viewer,
			ScalableFreeformRootEditPart root) {
		viewer.setProperty(MouseWheelHandler.KeyGenerator.getKey(SWT.MOD1), 
				MouseWheelZoomHandler.SINGLETON);
		
		List zoomLevels = new ArrayList(3);
		zoomLevels.add(ZoomManager.FIT_ALL);
		zoomLevels.add(ZoomManager.FIT_WIDTH);
		zoomLevels.add(ZoomManager.FIT_HEIGHT);
		
		zoomManager = root.getZoomManager();
		//zoomManager.setZoomAnimationStyle(ZoomManager.ANIMATE_ZOOM_IN_OUT);
		zoomManager.setZoomLevelContributions(zoomLevels);

		// Keyboard zoom
		IAction zoomIn = new ZoomInAction(root.getZoomManager());
		IAction zoomOut = new ZoomOutAction(root.getZoomManager());
		getActionRegistry().registerAction(zoomIn);
		getActionRegistry().registerAction(zoomOut);
		getSite().getKeyBindingService().registerAction(zoomIn);
		getSite().getKeyBindingService().registerAction(zoomOut);
		
	}

	@Override
	public void createActions() {
		super.createActions();
		EditorUtil.createActions(this.getActionRegistry(), this);
	}
	
	@Override
	public boolean isDirty() {
		return true;
	}
	
	@Override
	public void dispose() {
		getEditDomain().setActiveTool(null);
		adapterFactory.dispose();
		PALETTE_MODEL = null;
		
		UninstallListeners();
		
		pbModelManager = null;
		Debug.log_warning(PosterboardPlugin.PLUGIN_ID, "pbModelManager set to null");
		
		EventManager.getInstance().unSubscribeTo(EventIDs.MODEL_REWIND, updateHandler);
		EventManager.getInstance().unSubscribeTo(EventIDs.MODEL_UPDATE_EVENT2, updateHandler);
		updateHandler= null;
		
		if(genericHandler != null) {
			EventManager.getInstance().unSubscribeTo(EventIDs.RECEIVE_PEER_EVENT, genericHandler);
		}
		genericHandler = null;
		
		super.dispose();
		try {
			this.finalize();
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}

	public void forceFileDirty() {
		firePropertyChange(PROP_DIRTY);
	}
	
	@Override
	public void doSave(IProgressMonitor monitor) {
		firePropertyChange(PROP_DIRTY);
		pbModelManager.saveResources(monitor);
		getCommandStack().markSaveLocation();
	}
	
	public ActionRegistry getActionRegistry() {
		return this.pbActionRegistry;
	}
	
	@SuppressWarnings("unchecked")
	public Object getAdapter(Class type) {
		if (type == IContentOutlinePage.class){
			return new PosterboardTreeOutlinePage(
						new TreeViewer(), 
						getEditDomain(), 
						pbModelManager,
						(ActionRegistry) this.getAdapter(ActionRegistry.class));
		}
		
		if(type == PosterBoardModelManager.class) {
			return getModelManager();
		}
		
		if (type == ActionRegistry.class) {
			return getActionRegistry();
		}

		// get the command stack associated to this editor
		if (type == CommandStack.class){
			if(getEditDomain() != null)
				return getEditDomain().getCommandStack();
		}
		
		if (type == EditDomain.class) {
			return getEditDomain();
		}
		
		if (type == ModelManager.class) {
			return pbModelManager.getPapyrusModelManager();
		}
		
		if (type == IPropertySheetPage.class) {
			return new TabbedPropertySheetPage(this);
		}
		
		if (type == ZoomManager.class) {
			return zoomManager;
		}
		
		return super.getAdapter(type);
	}
	
	protected KeyHandler getCommonKeyHandler(){
		if (sharedKeyHandler == null){
			sharedKeyHandler = new PapyrusGraphicalViewerKeyHandler(this.getGraphicalViewer());
			// ctrl+l = adapt dimension of the figure
			sharedKeyHandler.put(
					KeyStroke.getPressed('\f', 108, SWT.CTRL), getActionRegistry().getAction(AdaptDimension.ADAPT_DIMENSION_ACTION_ID));
			sharedKeyHandler.put(
					KeyStroke.getPressed(SWT.F2, 0), getActionRegistry().getAction(GEFActionConstants.DIRECT_EDIT));
			sharedKeyHandler.put(
					KeyStroke.getPressed(SWT.DEL, 127, SWT.SHIFT), getActionRegistry().getAction(DeleteFromModelAction.DELETE_FROM_MODEL_ACTION_ID));
			sharedKeyHandler.put(
					KeyStroke.getPressed(SWT.DEL, 127, 0), getActionRegistry().getAction(DeleteFromPosterboardAction.DELETE_FROM_DIAGRAM_ACTION_ID));
		}
		return sharedKeyHandler;
	}

	public String getContributorId() {
		return "TreeOutlinePage"; //$NON-NLS-1$
	}

	protected Posterboard getModel() {
		return posterboard;
	}
	
	public PosterBoardModelManager getModelManager() {
		return pbModelManager;
	}
	
	@Override
	protected PaletteRoot getPaletteRoot() {
		if (PALETTE_MODEL == null)
			PALETTE_MODEL = PosterBoardEditorPaletteFactory.createPalette();
		return PALETTE_MODEL;
	}

	@SuppressWarnings("unchecked")
	public List getSelectionActions() {
		return super.getSelectionActions();
	}
	
	private void InstallListeners() {
		boolean isFacilitator = TeamList.getInstance().getMe().isFacilitator();
		
		if(pbModelManager == null) {
			//throw new RuntimeException("pbModelManager null");
			Debug.log_error(PosterboardPlugin.PLUGIN_ID, "pbModelManager null ");
		}

		dListener = new DiagramListener(pbModelManager, this.serialNumber);
		if (isFacilitator) {
			pbModelManager.getPapyrusModelManager().
					getModelManagerListener().addPropertyChangeListener(dListener);
		} 
		else {
			pbModelManager.getPapyrusModelManager().
					getDiResource().eAdapters().add(dListener);		
		}
	}
	
	private void UninstallListeners() {
		if(roleInitialized) {
			if (wasFacilitator) {
				pbModelManager.getPapyrusModelManager().
					getModelManagerListener().removePropertyChangeListener(dListener);
			} 
			else {
				pbModelManager.getPapyrusModelManager().
					getDiResource().eAdapters().remove(dListener);
			}
		}
	}
	
	private class NotifySessionRoleChangeEventHandler implements ISubscriber {

		@Override
		public void notifyEvent(IEvent e) {

			if (e.getEventID().equalsIgnoreCase(EventIDs.NOTIFY_SESSION_ROLE_CHANGE_EVENT)) {
				FacilitatorChange ();
			}
		}
	}

	private NotifySessionRoleChangeEventHandler roleChangeHandler = new NotifySessionRoleChangeEventHandler();
	private boolean roleInitialized = false;
	private boolean wasFacilitator = false;
	private void FacilitatorChange () {
		
		// check if we are online
		if( !inSession()) {
			return;
		}
		
		boolean nowFacilitator = TeamList.getInstance().getMe().isFacilitator();
		Debug.log_info(PosterboardPlugin.PLUGIN_ID, "FacilitatorChange " + nowFacilitator );
		
		if(! roleInitialized) {
			roleInitialized = true;
			InstallListeners();
		}
		else if(wasFacilitator != nowFacilitator) {
			UninstallListeners();
			InstallListeners();
		}
		
		wasFacilitator = nowFacilitator;
	}
	
	@Override
	public void init(IEditorSite site, IEditorInput input)
			throws PartInitException {
		
		initializeSession();
		
		this.getEditDomain().setCommandStack(
				new CamelCommandStack(pbModelManager, serialNumber));
		super.init(site, input);
			
	}
	
	private void initializeTheGenericHandler() {
		if (!inSession()) {
			return;
		}
		final List<String> eventIDs = new ArrayList<String>();
		eventIDs.add(EventIDs.RECEIVE_PEER_EVENT);
		
		// We need this because we want the updates to start only
		// after the editor has opened.
		Display.getDefault().asyncExec(new Runnable() {
		
			@Override
			public void run() {
				if ( ! TeamList.getInstance().isPlayback() ) {
					EventManager.getInstance().initializeSubscriber(genericHandler, eventIDs, true);
				}
				else {
					EventManager.getInstance().subscribeTo(EventIDs.MODEL_REWIND, updateHandler);
					
					EventManager.getInstance().subscribeTo(EventIDs.PLACABACK_POST, updateHandler);
				}
		
				EventManager.getInstance().subscribeTo(EventIDs.NOTIFY_SESSION_ROLE_CHANGE_EVENT, roleChangeHandler);
				
				EventManager.getInstance().subscribeTo(EventIDs.MODEL_UPDATE_EVENT2, updateHandler);

				FacilitatorChange ();
			}
		});
				
	}

	@Override
	protected void initializeGraphicalViewer() {
		GraphicalViewer viewer = getGraphicalViewer();
		viewer.getControl().addDisposeListener(new org.eclipse.swt.events.DisposeListener() {
			public void widgetDisposed(org.eclipse.swt.events.DisposeEvent e) {
			}
		});
		
		viewer.getControl().setBackground(diagramColor);
		
		// DND setup
		viewer.addDropTargetListener(new PBUMLDiagramDropTargetListener(viewer));
		//viewer.addDropTargetListener(new PBWBDiagramDropTargetListener(viewer));
		
		// set the contents of this editor
		// This starts the creation of editparts
		viewer.setContents(getModel()); 
		
		initializeTheGenericHandler();
	}

	public boolean inSession() {
		if (TeamList.getInstance().getMyStatus() == TEAMMEMBER_STATUS.SESSION) {
			return true;
		}
		return false;
	}
	
	public boolean initializeSession() {
		if (TeamList.getInstance().getMyStatus() == TEAMMEMBER_STATUS.OFFLINE) {
				EditorUtil.closeEditor(null, this, false);
				Debug.log_error(PosterboardPlugin.PLUGIN_ID, 
						Messages.getString("PosterBoardEditor.NOT_LOGGED_IN"));
		} else if (TeamList.getInstance().getMyStatus() == TEAMMEMBER_STATUS.ONLINE) {
			EditorUtil.closeEditor(null, this, false);
			Debug.log_error(PosterboardPlugin.PLUGIN_ID, 
					Messages.getString("PosterBoardEditor.NOT_IN_SESSION"));
		} else {
			return true;
		}
		return false;
	}
	
	@Override
	public boolean isSaveAsAllowed() {
		return false;
	}
	
	@Override
	protected void setInput(IEditorInput input) {
		super.setInput(input);
		IFile file = ((IFileEditorInput) input).getFile();
		// Set editor name
		setPartName(file.getName());
		// Load resources
		pbModelManager.loadResources(file);
		posterboard = pbModelManager.getPosterBoard();
	}
	
}
